//
// Created by LvenLiu on 2022/8/16.
//



#include "VideoChannel.h"


VideoChannel::VideoChannel(int stream_index, AVCodecContext *context) : BaseChannel(stream_index,
                                                                                    context) {
}

void *task_packet_decode(void *args) {
    VideoChannel *channel = reinterpret_cast<VideoChannel *>(args);
    channel->packet_decode();
    return nullptr;
}

/**
 * AVPacket解压缩
 */
void VideoChannel::packet_decode() {
    LOGE("packet_decode start ...");
    AVPacket *packet = nullptr;
    while (playing) {
        int r = packets.pull(packet);

        if (r == 0) {
            // 没有数据，也要继续等待
            continue;
        }
        if (!playing) {
            // 如果已经停止
            break;
        }
        // 将压缩包发送给ffmpeg 解压
        r = avcodec_send_packet(codecContext, packet);// 0 is success
        // 回收包 ffmpeg 里面会缓存
        releaseAVPacket(&packet);

        if (r != 0) { // r!=0
            // 发送出现错误
            break;
        }
        // 这个要开空间
        AVFrame * frame = av_frame_alloc();
        r = avcodec_receive_frame(codecContext, frame);
        if (r == AVERROR(EAGAIN)) {
            // B帧 B帧参考前面成功  B帧参考后面失败   可能是P帧还没有出来， 你等等  你再拿一次 可能就拿到了
            continue;
        } else if (r != 0) {
            // 出现错误
            break;
        }

        // 拿到解压数据（原始数据）
        addAVFrame(frame);

    }
    // 结束也要回收
    releaseAVPacket(&packet);
    LOGE("packet_decode end ...");
}


void *task_frame_play(void *args) {
    VideoChannel *channel = reinterpret_cast<VideoChannel *>(args);
    channel->frame_play();
    return nullptr;
}

/**
 * 播放
 * 1. 将YUV转成RGBA
 * 2. 原始包（YUV数据） -----> [libswscale]  Android屏幕（RGBA数据）
 */
void VideoChannel::frame_play() {

    // 从队列取出播放
    AVFrame *frame = nullptr;
    uint8_t *dst_data[4]; // RGBA
    int dst_line_size[4]; // RGBA


    // 视频宽高
    int width = codecContext->width;
    int height = codecContext->height;
    // 原始包（YUV数据） -----> [libswscale]  Android屏幕（RGBA数据）
    //给 dst_data 申请内存   width * height * 4
    av_image_alloc(dst_data, dst_line_size, width, height,
                   AV_PIX_FMT_RGBA, 1);

    // yuv -> rgba 上下文
    SwsContext *swsContext = sws_getContext(
            // 输入:原视频宽高 视频编码格式
            width, height, codecContext->pix_fmt,
            // 输出：输出视频的宽高 输出的编码格式
            width, height, AV_PIX_FMT_RGBA, SWS_BILINEAR,
            nullptr, nullptr, nullptr
    );
    LOGE("frame_play ");
    while (playing) {
        int r = frames.pull(frame);
        if (r == 0) {
            // 还没有数据，等一下
            continue;
        }
        if (!playing) {
            // 停止了
            break;
        }
        // 格式转换 yuv ----> rgba
        sws_scale(
                swsContext,
                // 下面是 输入环节  YUV的数据
                frame->data, frame->linesize,
                0, codecContext->height,

                // 下面是 输出环节 成果：RGBA数据 Android SurfaceView播放画面
                dst_data,
                dst_line_size
        );
        // 回调出去渲染 typedef void (*RenderCallback)(uint8_t*,int,int,int);
        renderCallback(dst_data[0], width, height, dst_line_size[0]);
        // 回收
        releaseAVFrame(&frame);
    }
    playing = false;
    // 回收

    if (frame){
        releaseAVFrame(&frame);
    }
    // 回收
    sws_freeContext(swsContext);
    // 释放dst_data
    //av_free(dst_data);
    // av_free(&dst_data[0]);
    LOGE("frame_play end ...");
}

void VideoChannel::start() {
    playing = true;
    packets.setWork(1); // 压缩包 队列
    frames.setWork(1);// 原始包（压缩包解压） 队列
    // 第一个线程： 视频：取出队列的压缩包 进行解码 解码后的原始包 再push队列中去
    pthread_create(&pid_packet_decode, nullptr, task_packet_decode, this);

    // 第二线线程：视频：从队列取出原始包，播放
    pthread_create(&pid_frame_play, nullptr, task_frame_play, this);
}

void VideoChannel::stop() {
    playing = false;
    packets.stopAndClear(); // 压缩包 队列
    frames.stopAndClear();// 原始包（压缩包解压） 队列
}

void VideoChannel::setRenderCallback(RenderCallback renderCallback) {
    this->renderCallback = renderCallback;
}

